/*
 * @[H]:  Copyright (c) 2021 Phytium Information Technology, Inc. 
 * 
 *  SPDX-License-Identifier: Apache-2.0. 
 * 
 * @Date: 2021-07-13 16:48:42
 * @LastEditTime: 2021-08-16 15:28:33
 * @Description:  Description of file
 * @Modify History: 
 * * * Ver   Who        Date         Changes
 * * ----- ------     --------    --------------------------------------
 */
#include "f_spi.h"
#include "f_spi_hw.h"
#include "kernel.h"
#include <string.h>
#include "ft_debug.h"
#include "fsleep.h"
#define FSPI_DEBUG_TAG "FSPI"
#define FSPI_ERROR(format, ...) FT_DEBUG_PRINT_E(FSPI_DEBUG_TAG, format, ##__VA_ARGS__)
#define FSPI_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FSPI_DEBUG_TAG, format, ##__VA_ARGS__)
#define FSPI_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FSPI_DEBUG_TAG, format, ##__VA_ARGS__)

extern void
FSpiHwInit(FSpi *instance_p);

static _INLINE u32 TxMax(FSpi *instance_p)
{
    FSpiConfig *config_p;
    u32 tx_left, tx_room, rxtx_gap;
    config_p = &instance_p->config;
    tx_left = ((fsize_t)instance_p->send_buffer_end_p - (fsize_t)instance_p->send_buffer_p) / config_p->n_bytes;
    tx_room = config_p->tx_fifo_len - FSPI_READREG32(config_p->base_address, FSPI_TXFLR_OFFSET);
    rxtx_gap = (((fsize_t)instance_p->recv_buffer_end_p - (fsize_t)instance_p->recv_buffer_p) - ((fsize_t)instance_p->send_buffer_end_p - (fsize_t)instance_p->send_buffer_p)) / config_p->n_bytes;
    return min3(tx_left, tx_room, (u32)(config_p->tx_fifo_len - rxtx_gap));
}

static _INLINE u32 RxMax(FSpi *instance_p)
{
    FSpiConfig *config_p;
    u32 rx_left;
    config_p = &instance_p->config;
    rx_left = ((fsize_t)instance_p->recv_buffer_end_p - (fsize_t)instance_p->recv_buffer_p) / config_p->n_bytes;
    return min(rx_left, FSPI_READREG32(config_p->base_address, FSPI_RXFLR_OFFSET));
}

static void FSpiWrite(FSpi *instance_p)
{
    FT_ASSERTVOID(instance_p != NULL);
    FT_ASSERTVOID(instance_p->is_ready == FT_COMPONENT_IS_READY);
    FSpiConfig *config_p;
    u32 max;
    u16 txw = 0;
    config_p = &instance_p->config;
    max = TxMax(instance_p);
    while (max--)
    {
        //if send_buffer_p is null , Can't use this pointer
        if ((fsize_t)instance_p->send_buffer_end_p - instance_p->length)
        {
            if (config_p->n_bytes == 1)
            {
                txw = *(u8 *)(instance_p->send_buffer_p);
            }
            else if (config_p->n_bytes == 2)
            {
                txw = *(u16 *)(instance_p->send_buffer_p);
            }
            else
            {
                txw = *(u8 *)(instance_p->send_buffer_p);
            }
        }

        FSPI_WRITEREG32(config_p->base_address, FSPI_DR_OFFSET, txw);
        instance_p->send_buffer_p += config_p->n_bytes;
    }
}

static void FSpiRead(FSpi *instance_p)
{
    FT_ASSERTVOID(instance_p != NULL);
    FT_ASSERTVOID(instance_p->is_ready == FT_COMPONENT_IS_READY);
    FSpiConfig *config_p;
    u32 max;
    u16 rxw = 0;
    config_p = &instance_p->config;
    max = RxMax(instance_p);
    while (max--)
    {
        rxw = FSPI_READREG32(config_p->base_address, FSPI_DR_OFFSET);
        if ((fsize_t)instance_p->recv_buffer_end_p - instance_p->length)
        {
            if (config_p->n_bytes == 1)
            {
                *(u8 *)instance_p->recv_buffer_p = rxw;
            }
            else if (config_p->n_bytes == 2)
            {
                *(u16 *)instance_p->recv_buffer_p = rxw;
            }
            else
            {
                *(u8 *)instance_p->recv_buffer_p = rxw;
            }
        }
        instance_p->recv_buffer_p += config_p->n_bytes;
    }
}

void FSpiIrqTransfer(FSpi *instance_p, u16 irq_status)
{
    FT_ASSERTVOID(instance_p != NULL);
    FT_ASSERTVOID(instance_p->is_ready == FT_COMPONENT_IS_READY);
    FSpiConfig *config_p;
    config_p = &instance_p->config;
    if (irq_status & ((INT_TXOI | INT_RXOI | INT_RXUI)))
    {
        FSPI_DEBUG_W("interrupt_transfer: fifo overrun/underrun");
        if (instance_p->fspi_full_callback)
        {
            instance_p->fspi_full_callback(instance_p->full_args);
        }
        return;
    }

    FSpiRead(instance_p);

    if (instance_p->recv_buffer_end_p == instance_p->recv_buffer_p)
    {
        FSpiIrqMask(config_p->base_address, INT_TXEI);
        if (instance_p->fspi_recv_fin_callback)
        {
            instance_p->fspi_recv_fin_callback(instance_p->recv_args);
        }
        return;
    }

    if (irq_status & INT_TXEI)
    {
        FSpiIrqMask(config_p->base_address, INT_TXEI);
        FSpiWrite(instance_p);
        FSpiIrqUmask(config_p->base_address, INT_TXEI);
    }
}

static void FSpiPollTransfer(FSpi *instance_p)
{
    do
    {
        FSpiWrite(instance_p);
        FSpiRead(instance_p);
    } while (instance_p->recv_buffer_end_p > instance_p->recv_buffer_p);
}

ft_error_t FSpiTransfer(FSpi *instance_p, void *tx_buf, void *rx_buf, fsize_t length)
{
    FT_ASSERTZERONUM(instance_p != NULL);
    FT_ASSERTZERONUM(instance_p->is_ready == FT_COMPONENT_IS_READY);
    FSpiConfig *config_p;
    u32 cr0, tmode;
    u16 txlevel;
    u8 imask = 0;
    if (instance_p->is_busy)
    {
        FSPI_DEBUG_W("FSpiTransfer is busy");
        return FSPI_BUSY_STATUS;
    }
    config_p = &instance_p->config;

    FSpiEnableChip(config_p->base_address, 0);

    cr0 = ((config_p->n_bytes << 3) - 1) | (config_p->spi_trans_mode << MODE_OFFSET);
    if (tx_buf && rx_buf)
        tmode = SPI_TMOD_TR;
    else if (rx_buf)
        tmode = SPI_TMOD_RO;
    else
        tmode = SPI_TMOD_TR;

    cr0 |= (tmode << SPI_TMOD_OFFSET);
    FSPI_WRITEREG32(config_p->base_address, FSPI_CTRL_R0_OFFSET, cr0);

    FSpiIrqMask(config_p->base_address, 0xff);

    instance_p->is_busy = 1;
    instance_p->length = length;
    instance_p->send_buffer_p = tx_buf;
    instance_p->send_buffer_end_p = instance_p->send_buffer_p + length;
    instance_p->recv_buffer_p = rx_buf;
    instance_p->recv_buffer_end_p = instance_p->recv_buffer_p + length;

    if (config_p->work_mode == FSPI_INTR_TRANSACTION)
    {
        txlevel = min(config_p->tx_fifo_len / 2, instance_p->length / config_p->n_bytes);
        FSPI_WRITEREG32(config_p->base_address, FSPI_TXFTL_R_OFFSET, txlevel);
        imask |= INT_TXEI | INT_TXOI | INT_RXUI | INT_RXOI;
        FSpiIrqUmask(config_p->base_address, imask);
        FSpiEnableChip(config_p->base_address, 1);
        instance_p->is_busy = 0;
    }
    else
    {
        FSpiEnableChip(config_p->base_address, 1);
        FSpiPollTransfer(instance_p);
        instance_p->is_busy = 0;
    }

    return FT_SUCCESS;
}

ft_error_t FSpiCfgInitialize(FSpi *instance_p, FSpiConfig *config_p)
{
    FT_ASSERTZERONUM(instance_p != NULL);
    memset(instance_p, 0, sizeof(FSpi));
    instance_p->config = *config_p;
    FSpiHwInit(instance_p);
    instance_p->is_ready = FT_COMPONENT_IS_READY;
    FSpiSetOptions(instance_p, FSPI_OPTION_SPEED, instance_p->config.module_frequency);
    return FT_SUCCESS;
}
